package cl.regalloc.parse;

import java.util.HashSet;

public class Opcode {
	public static final String OP_SLL = "sll";
	public static final String OP_SRL = "srl";
	public static final String OP_SRA = "sra";
	public static final String OP_SLLV = "sllv";
	public static final String OP_SRLV = "srlv";
	public static final String OP_SRAV = "srav";
	public static final String OP_JR = "jr";
	public static final String OP_JALR = "jalr";
	public static final String OP_SYSCALL = "syscall";
	public static final String OP_BREAK = "break";
	public static final String OP_MFHI = "mfhi";
	public static final String OP_MTHI = "mthi";
	public static final String OP_MFLO = "mflo";
	public static final String OP_MTLO = "mtlo";
	public static final String OP_MULT = "mult";
	public static final String OP_MULTU = "multu";
	public static final String OP_DIV = "div";
	public static final String OP_DIVU = "divu";
	public static final String OP_ADD = "add";
	public static final String OP_ADDU = "addu";
	public static final String OP_SUB = "sub";
	public static final String OP_SUBU = "subu";
	public static final String OP_AND = "and";
	public static final String OP_OR = "or";
	public static final String OP_XOR = "xor";
	public static final String OP_NOR = "nor";
	public static final String OP_SLT = "slt";
	public static final String OP_SLTU = "sltu";

	// conditionals
	public static final String OP_BLTZ = "bltz";
	public static final String OP_BGEZ = "bgez";
	public static final String OP_BLTZAL = "bltzal";
	public static final String OP_BGEZAL = "bgezal";

	public static final String OP_J = "j";
	public static final String OP_JAL = "jal";
	public static final String OP_BEQ = "beq";
	public static final String OP_BNE = "bne";
	public static final String OP_BLEZ = "blez";
	public static final String OP_BGTZ = "bgtz";
	public static final String OP_ADDI = "addi";
	public static final String OP_ADDIU = "addiu";
	public static final String OP_SLTI = "slti";
	public static final String OP_SLTIU = "sltiu";
	public static final String OP_ANDI = "andi";
	public static final String OP_ORI = "ori";
	public static final String OP_XORI = "xori";
	public static final String OP_LUI = "lui";




	public static final String OP_MFCZ = "mfcz";
	public static final String OP_MTCZ = "mtcz";
	public static final String OP_CFCZ = "cfcz";
	public static final String OP_CTCZ = "ctcz";
	public static final String OP_BCZF = "bczf";
	public static final String OP_BCZT = "bczt";
	public static final String OP_TLBR = "tlbr";
	public static final String OP_TLBWI = "tlbwi";
	public static final String OP_TLBWR = "tlbwr";
	public static final String OP_TLBP = "tlbp";
	public static final String OP_RFE = "rfe";

	public static final String OP_COP0 = "cop0";
	public static final String OP_MFC0 = "mfc0";
	public static final String OP_MTC0 = "mtc0";
	public static final String OP_CFC0 = "cfc0";
	public static final String OP_CTC0 = "ctc0;";
	public static final String OP_BC0F = "bc0f";
	public static final String OP_BC0T = "bc0t";

	public static final String OP_COP1 = "cop1";
	public static final String OP_MFC1 = "mfc1";
	public static final String OP_MTC1 = "mtc1";
	public static final String OP_CFC1 = "cfc1";
	public static final String OP_CTC1 = "ctc1";
	public static final String OP_BC1F = "bc1f";
	public static final String OP_BC1T = "bc1t";

	public static final String OP_ADD_S = "add.s";
	public static final String OP_SUB_S = "sub.s";
	public static final String OP_MUL_S = "mul.s";
	public static final String OP_DIV_S = "div.s";
	public static final String OP_ABS_S = "abs.s";
	public static final String OP_MOV_S = "mov.s";
	public static final String OP_NEG_S = "neg.s";
	public static final String OP_CVT_S_S = "cvt.s.s"; /* This one does not really exist */
	public static final String OP_CVT_D_S = "cvt.d.s";
	public static final String OP_CVT_W_S = "cvt.w.s";
	public static final String OP_C_F_S = "c.f.s";
	public static final String OP_C_UN_S = "c.un.s";
	public static final String OP_C_EQ_S = "c.eq.s";
	public static final String OP_C_UEQ_S = "c.ueq.s";
	public static final String OP_C_OLT_S = "c.olt.s";
	public static final String OP_C_ULT_S = "c.ult.s";
	public static final String OP_C_OLE_S = "c.ole.s";
	public static final String OP_C_ULE_S = "c.ule.s";
	public static final String OP_C_SF_S = "c.sf.s";
	public static final String OP_C_NGLE_S = "c.ngle.s";
	public static final String OP_C_SEQ_S = "c.seq.s";
	public static final String OP_C_NGL_S = "c.ngl.s";
	public static final String OP_C_LT_S = "c.lt.s";
	public static final String OP_C_NGE_S = "c.nge.s";
	public static final String OP_C_LE_S = "c.le.s";
	public static final String OP_C_NGT_S = "c.ngt.s";
	public static final String OP_ADD_D = "add.d";
	public static final String OP_SUB_D = "sub.d";
	public static final String OP_MUL_D = "mul.d";
	public static final String OP_DIV_D = "div.d";
	public static final String OP_ABS_D = "abs.d";
	public static final String OP_MOV_D = "mov.d";
	public static final String OP_NEG_D = "neg.d";
	public static final String OP_CVT_S_D = "cvt.s.d";
	public static final String OP_CVT_W_D = "cvt.w.d";
	public static final String OP_C_F_D = "c.f.d";
	public static final String OP_C_UN_D = "c.un.d";
	public static final String OP_C_EQ_D = "c.eq.d";
	public static final String OP_C_UEQ_D = "c.ueq.d";
	public static final String OP_C_OLT_D = "c.olt.d";
	public static final String OP_C_ULT_D = "c.ult.d";
	public static final String OP_C_OLE_D = "c.ole.d";
	public static final String OP_C_ULE_D = "c.ule.d";
	public static final String OP_C_SF_D = "c.sf.d";
	public static final String OP_C_NGLE_D = "c.ngle.d";
	public static final String OP_C_SEQ_D = "c.seq.d";
	public static final String OP_C_NGL_D = "c.ngl.d";
	public static final String OP_C_LT_D = "c.lt.d";
	public static final String OP_C_NGE_D = "c.nge.d";
	public static final String OP_C_LE_D = "c.le.d";
	public static final String OP_C_NGT_D = "c.ngt.d";

	public static final String OP_ADD_W = "add.w"; /* This one does not exist */
	public static final String OP_CVT_S_W = "cvt.s.w";
	public static final String OP_CVT_D_W = "cvt.d.w";
	public static final String OP_CVT_W_W = "cvt.w.w"; /* This one does not really exist */


	public static final String OP_COP2 = "cop2";
	public static final String OP_MFC2 = "mfc2";
	public static final String OP_MTC2 = "mtc2";
	public static final String OP_CFC2 = "cfc2";
	public static final String OP_CTC2 = "ctc2";
	public static final String OP_BC2F = "bc2f";
	public static final String OP_BC2T = "bc2t";
	public static final String OP_COP3 = "cop3";
	public static final String OP_MFC3 = "mfc3";
	public static final String OP_MTC3 = "mtc3";
	public static final String OP_CFC3 = "cfc3";
	public static final String OP_CTC3 = "ctc3";
	public static final String OP_BC3F = "bc3f";
	public static final String OP_BC3T = "bc3t";

	public static final String OP_LB = "lb";
	public static final String OP_LH = "lh";
	public static final String OP_LWL = "lwl";
	public static final String OP_LW = "lw";
	public static final String OP_LBU = "lbu";
	public static final String OP_LHU = "lhu";
	public static final String OP_LWR = "lwr";

	public static final String OP_SB = "sb";
	public static final String OP_SH = "sh";
	public static final String OP_SWL = "swl";
	public static final String OP_SW = "sw";

	public static final String OP_SWR = "swr";

	public static final String OP_LWC0 = "lwc0";
	public static final String OP_LWC1 = "lwc1";
	public static final String OP_LWC2 = "lwc2";
	public static final String OP_LWC3 = "lwc3";

	public static final String OP_SWC0 = "swc0";
	public static final String OP_SWC1 = "swc1";
	public static final String OP_SWC2 = "swc2";
	public static final String OP_SWC3 = "swc3";

	public static final String OP_PSEUDOADDIU_PC = "pseudoaddiu.pc"; /* AM MIPS16 hack */
	public static final String OP_PSEUDOLW_PC = "pseudolw.pc"; /* AM MIPS16 hack */

	public static final String OP_NOOP = "noop";





	private static boolean isOpcode(String opcode) {
		if ((opcode.equals(OP_SLL)) || (opcode.equals(OP_SRL)) || (opcode.equals(OP_SRA)) || (opcode.equals(OP_SLLV)) || (opcode.equals(OP_SRLV)) || (opcode.equals(OP_SRAV))
				|| (opcode.equals(OP_JR)) || (opcode.equals(OP_JALR)) || (opcode.equals(OP_SYSCALL)) || (opcode.equals(OP_BREAK)) || (opcode.equals(OP_MFHI)) || (opcode.equals(OP_MTHI))
				|| (opcode.equals(OP_MFLO)) || (opcode.equals(OP_MTLO)) || (opcode.equals(OP_MULT)) || (opcode.equals(OP_MULTU)) || (opcode.equals(OP_DIV)) || (opcode.equals(OP_DIVU))
				|| (opcode.equals(OP_ADD)) || (opcode.equals(OP_ADDU)) || (opcode.equals(OP_SUB)) || (opcode.equals(OP_SUBU)) || (opcode.equals(OP_AND)) || (opcode.equals(OP_OR))
				|| (opcode.equals(OP_XOR)) || (opcode.equals(OP_NOR)) || (opcode.equals(OP_SLT)) || (opcode.equals(OP_SLTU))) return true;

		if ((opcode.equals(OP_BLTZ)) || (opcode.equals(OP_BGEZ)) || (opcode.equals(OP_BLTZAL)) || (opcode.equals(OP_BGEZAL)) || (opcode.equals(OP_J)) || (opcode.equals(OP_JAL))
				|| (opcode.equals(OP_BEQ)) || (opcode.equals(OP_BNE)) || (opcode.equals(OP_BLEZ)) || (opcode.equals(OP_BGTZ)) || (opcode.equals(OP_ADDI)) || (opcode.equals(OP_ADDIU))
				|| (opcode.equals(OP_SLTI)) || (opcode.equals(OP_SLTIU)) || (opcode.equals(OP_ANDI)) || (opcode.equals(OP_ORI)) || (opcode.equals(OP_XORI)) || (opcode.equals(OP_LUI))) return true;

		if ((opcode.equals(OP_MFCZ)) || (opcode.equals(OP_MTCZ)) || (opcode.equals(OP_CFCZ)) || (opcode.equals(OP_CTCZ)) || (opcode.equals(OP_BCZF)) || (opcode.equals(OP_BCZT)) || (opcode.equals(OP_TLBR))
				|| (opcode.equals(OP_TLBWI)) || (opcode.equals(OP_TLBWI)) || (opcode.equals(OP_TLBWR)) || (opcode.equals(OP_TLBP)) || (opcode.equals(OP_RFE)) || (opcode.equals(OP_ADD_W))
				|| (opcode.equals(OP_CVT_S_W)) || (opcode.equals(OP_CVT_D_W)) || (opcode.equals(OP_CVT_W_W))) return true;

		if ((opcode.equals(OP_LB)) || (opcode.equals(OP_LH)) || (opcode.equals(OP_LWL)) || (opcode.equals(OP_LW)) || (opcode.equals(OP_LBU)) || (opcode.equals(OP_LHU)) || (opcode.equals(OP_LWR))
				|| (opcode.equals(OP_SB)) || (opcode.equals(OP_SH)) || (opcode.equals(OP_SWL)) || (opcode.equals(OP_SW)) || (opcode.equals(OP_SWR)) || (opcode.equals(OP_LWC0)) || (opcode.equals(OP_LWC1))
				|| (opcode.equals(OP_LWC2)) || (opcode.equals(OP_LWC3)) || (opcode.equals(OP_SWC0)) || (opcode.equals(OP_SWC1)) || (opcode.equals(OP_SWC2)) || (opcode.equals(OP_SWC3))
				|| (opcode.equals(OP_PSEUDOADDIU_PC)) || (opcode.equals(OP_PSEUDOLW_PC)) || (opcode.equals(OP_NOOP))) return true;


		if ((opcode.equals(OP_ADD_S)) || (opcode.equals(OP_SUB_S)) || (opcode.equals(OP_MUL_S)) || (opcode.equals(OP_DIV_S)) || (opcode.equals(OP_ABS_S)) || (opcode.equals(OP_MOV_S))
				|| (opcode.equals(OP_NEG_S)) || (opcode.equals(OP_CVT_S_S)) || (opcode.equals(OP_CVT_D_S)) || (opcode.equals(OP_CVT_W_S)) || (opcode.equals(OP_C_F_S)) || (opcode.equals(OP_C_UN_S))
				|| (opcode.equals(OP_C_EQ_S)) || (opcode.equals(OP_C_UEQ_S)) || (opcode.equals(OP_C_OLT_S)) || (opcode.equals(OP_C_ULT_S)) || (opcode.equals(OP_C_OLE_S)) || (opcode.equals(OP_C_ULE_S))
				|| (opcode.equals(OP_C_SF_S)) || (opcode.equals(OP_C_NGLE_S)) || (opcode.equals(OP_C_SEQ_S)) || (opcode.equals(OP_C_NGL_S)) || (opcode.equals(OP_C_LT_S)) || (opcode.equals(OP_C_NGE_S))
				|| (opcode.equals(OP_C_LE_S)) || (opcode.equals(OP_C_NGT_S))) return true;

		if ((opcode.equals(OP_ADD_D)) || (opcode.equals(OP_SUB_D)) || (opcode.equals(OP_MUL_D)) || (opcode.equals(OP_DIV_D)) || (opcode.equals(OP_ABS_D)) || (opcode.equals(OP_MOV_D)) || (opcode.equals(OP_NEG_D))
				|| (opcode.equals(OP_CVT_S_D)) || (opcode.equals(OP_CVT_W_D)) || (opcode.equals(OP_C_F_D)) || (opcode.equals(OP_C_UN_D)) || (opcode.equals(OP_C_EQ_D)) || (opcode.equals(OP_C_UEQ_D))
				|| (opcode.equals(OP_C_OLT_D)) || (opcode.equals(OP_C_ULT_D)) || (opcode.equals(OP_C_OLE_D)) || (opcode.equals(OP_C_ULE_D)) || (opcode.equals(OP_C_SF_D)) || (opcode.equals(OP_C_NGLE_D))
				|| (opcode.equals(OP_C_SEQ_D)) || (opcode.equals(OP_C_NGL_D)) || (opcode.equals(OP_C_LT_D)) || (opcode.equals(OP_C_NGE_D)) || (opcode.equals(OP_C_LE_D))
				|| (opcode.equals(OP_C_NGT_D))) return true;

		if ((opcode.equals(OP_COP0)) || (opcode.equals(OP_MFC0)) || (opcode.equals(OP_MTC0)) || (opcode.equals(OP_CFC0)) || (opcode.equals(OP_CTC0)) || (opcode.equals(OP_BC0F)) || (opcode.equals(OP_BC0T))
				|| (opcode.equals(OP_COP1)) || (opcode.equals(OP_MFC1)) || (opcode.equals(OP_MTC1)) || (opcode.equals(OP_CFC1)) || (opcode.equals(OP_CTC1)) || (opcode.equals(OP_BC1F))
				|| (opcode.equals(OP_BC1T)) || (opcode.equals(OP_COP2)) || (opcode.equals(OP_MFC2)) || (opcode.equals(OP_MTC2)) || (opcode.equals(OP_CFC2)) || (opcode.equals(OP_CTC2))
				|| (opcode.equals(OP_BC2F)) || (opcode.equals(OP_BC2T)) || (opcode.equals(OP_COP3)) || (opcode.equals(OP_MFC3)) || (opcode.equals(OP_MTC3)) || (opcode.equals(OP_CFC3))
				|| (opcode.equals(OP_CTC3)) || (opcode.equals(OP_BC3F)) || (opcode.equals(OP_BC3T))) return true;

		return false;
	}

	public static HashSet<Register> identifyDefs(Instruction instruction) {
		HashSet<Register> rs = new HashSet<Register>();

		// opcode $r1, $r2, $r3
		if (instruction.getOpcode().equals(OP_OR) || instruction.getOpcode().equals(OP_SLT)
				|| instruction.getOpcode().equals(OP_ADDIU) || instruction.getOpcode().equals(OP_ORI)
				|| instruction.getOpcode().equals(OP_LW) || instruction.getOpcode().equals(OP_SLL)
				|| instruction.getOpcode().equals(OP_ADDU) || instruction.getOpcode().equals(OP_MFLO)
				|| instruction.getOpcode().equals(OP_SLTI)) {
			// $r1 defined
			rs.add((Register) instruction.getOp1());
			instruction.setOp1Def();
		}
		return rs;
	}

	public static HashSet<Register> identifyRefs(Instruction instruction) {
		HashSet<Register> rs = new HashSet<Register>();

		// opcode $r1, $r2, $r3
		if (instruction.getOpcode().equals(OP_BEQ) || instruction.getOpcode().equals(OP_BNE)
				|| instruction.getOpcode().equals(OP_MULT)) {
			// $r1, $r2 referenced
			rs.add((Register) instruction.getOp1());
			rs.add((Register) instruction.getOp2());
			instruction.setOp1Ref();
			instruction.setOp2Ref();
		} else if (instruction.getOpcode().equals(OP_OR) || instruction.getOpcode().equals(OP_SLT)
				|| instruction.getOpcode().equals(OP_ADDU)) {
			// $r2, $r3 referenced
			rs.add((Register) instruction.getOp2());
			rs.add((Register) instruction.getOp3());
			instruction.setOp2Ref();
			instruction.setOp3Ref();
		} else if (instruction.getOpcode().equals(OP_J) || instruction.getOpcode().equals(OP_SW)) {
			// $r1 referenced
			rs.add((Register) instruction.getOp1());
			instruction.setOp1Ref();
		} else if (instruction.getOpcode().equals(OP_ADDIU) || instruction.getOpcode().equals(OP_ORI)
				|| instruction.getOpcode().equals(OP_SLL) || instruction.getOpcode().equals(OP_SLTI)) {
			// $r2 referenced
			rs.add((Register) instruction.getOp2());
			instruction.setOp2Ref();
		} 
		return rs;
	}

	/** object-specific code **/

	private String opcode;

	public Opcode(String opcode) throws OpcodeDoesNotExistException {
		if (isOpcode(opcode))
			this.opcode = opcode;
		else throw new OpcodeDoesNotExistException();
	}

	public String toString() {
		return this.opcode;
	}

	public boolean isConditional() {
		if ((opcode.equals(OP_BLTZ)) || (opcode.equals(OP_BGEZ)) || (opcode.equals(OP_BLTZAL))
				|| (opcode.equals(OP_BGEZAL)) || (opcode.equals(OP_BEQ)) || (opcode.equals(OP_BNE))
				|| (opcode.equals(OP_BLEZ)) || (opcode.equals(OP_BGTZ))) return true;
		return false;
	}

	public boolean isJump() {
		if ((opcode.equals(OP_J)) || (opcode.equals(OP_JAL))) return true;
		return false;
	}

	public boolean isLoad() {
		if ((opcode.equals(OP_LB)) || (opcode.equals(OP_LH)) ||	(opcode.equals(OP_LWL))
				|| (opcode.equals(OP_LW)) || (opcode.equals(OP_LBU)) || (opcode.equals(OP_LHU))
				|| (opcode.equals(OP_LWR))) return true;
		return false;
	}

	public boolean equals(String str) {
		if (this.opcode.equals(str))
			return true;
		return false;
	}

	public boolean isMoveRelated() {
		if ((opcode.equals(OP_MFHI)) || (opcode.equals(OP_MFLO))) {
			return true;
		} else {
			return false;
		}
	}
}
